<?php

declare(strict_types=1);
/**
 * This file is part of MoChat.
 * @link     https://mo.chat
 * @document https://mochat.wiki
 * @contact  group@mo.chat
 * @license  https://github.com/mochat-cloud/mochat/blob/master/LICENSE
 */
namespace MoChat\Plugin\ChannelCode\Listener;

use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Utils\Codec\Json;
use MoChat\App\Corp\Utils\WeWorkFactory;
use MoChat\App\Medium\Constants\Type as MediumType;
use MoChat\App\Medium\Contract\MediumContract;
use MoChat\App\Utils\Media;
use MoChat\App\Utils\Url;
use MoChat\App\WorkContact\Contract\WorkContactContract;
use MoChat\App\WorkContact\Contract\WorkContactEmployeeContract;
use MoChat\App\WorkContact\Event\ContactWelcomeEvent;
use MoChat\App\WorkEmployee\Contract\WorkEmployeeContract;
use MoChat\Plugin\ChannelCode\Contract\ChannelCodeContract;
use MoChat\Plugin\Lottery\Contract\LotteryContract;

/**
 * 发送欢迎语监听.
 *
 * @Listener(priority=10)
 */
class SendWelcomeListener implements ListenerInterface
{
    /**
     * @Inject
     * @var WorkContactContract
     */
    protected $workContactService;

    /**
     * @Inject
     * @var WeWorkFactory
     */
    protected $weWorkFactory;

    /**
     * @Inject
     * @var WorkContactEmployeeContract
     */
    protected $workContactEmployeeService;

    /**
     * @Inject
     * @var WorkEmployeeContract
     */
    protected $workEmployeeService;

    /**
     * @Inject
     * @var LotteryContract
     */
    protected $lotteryService;

    /**
     * 渠道码表.
     * @Inject
     * @var ChannelCodeContract
     */
    private $channelCodeService;

    /**
     * @Inject
     * @var MediumContract
     */
    private $mediumService;

    /**
     * @Inject
     * @var StdoutLoggerInterface
     */
    private $logger;

    public function listen(): array
    {
        return [
            ContactWelcomeEvent::class,
        ];
    }

    /**
     * @param ContactWelcomeEvent $event
     */
    public function process(object $event)
    {
        try {
            $contact = $event->message;
            $employeeId = $contact['employeeId'];
            $corpId = $contact['corpId'];
            // 判断是否需要发送欢迎语
            if (! $this->isNeedSendWelcome($contact)) {
                return;
            }

            // 获取欢迎语
            $data = $this->getWelcome($contact);
            if (empty($data)) {
                $this->logger->debug(sprintf('[渠道活码]客户欢迎语未发送，获取欢迎语为空，客户id: %s', (string) $contact['id']));
                return;
            }

            $this->logger->debug(sprintf('[渠道活码]客户欢迎语匹配成功，即将发送，客户id: %s', (string) $contact['id']));

            // 发送欢迎语
            if (! empty($data['mediumId'])) {
                $medium = $this->getMediums($data['mediumId']);

                $medium[]['text'] = $data['text'];
                $weWorkContactApp = make(WeWorkFactory::class)->getContactApp($corpId);
                // 微信消息体
                $sendWelcomeData = $this->getSendWelcomeData($corpId, $contact, $medium);
                $sendWelcomeRes = $weWorkContactApp->external_contact_message->sendWelcome($contact['welcomeCode'], $sendWelcomeData);
                unset($data['mediumId']);
            } else {
                $this->workContactService->sendWelcome((int) $contact['corpId'], $contact, $contact['welcomeCode'], $data);
            }
        } catch (\Exception $e) {
            $this->logger->error(sprintf('[渠道活码]客户欢迎语发送失败，错误信息: %s', $e->getMessage()));
            $this->logger->error($e->getTraceAsString());
        }
    }

    /**
     * 获取欢迎语结构体.
     */
    private function getSendWelcomeData(int $corpId, array $contact, array $media): array
    {
        $sendWelcomeData = [];
        foreach ($media as $content) {
            $content = $this->replaceContactName($content, $contact['name']);

            // 微信消息体 - 文本
            empty($content['text']) || $sendWelcomeData['text']['content'] = $content['text'];

            // 微信消息体 - 媒体文件
            if (! empty($content['mediumType'])) {
                $mediaUtil = make(Media::class);
                // 组织推送消息数据
                switch ($content['mediumType']) {
                    case MediumType::PICTURE:
                        // 上传临时素材
                        $image['msgtype'] = 'image';
                        $image['image']['media_id'] = $mediaUtil->uploadImage($corpId, $content['mediumContent']['imagePath']);
                        $sendWelcomeData['attachments'][] = $image;
                        break;
                    case MediumType::PICTURE_TEXT:
                        $link['msgtype'] = 'link';
                        $link['link'] = [
                            'title' => $content['mediumContent']['title'],
                            'url' => $content['mediumContent']['imageLink'],
                        ];

                        if (isset($content['mediumContent']['imagePath'])) {
                            $link['link']['picurl'] = file_full_url($content['mediumContent']['imagePath']);
                        }

                        if (isset($content['mediumContent']['description'])) {
                            $link['link']['desc'] = $content['mediumContent']['description'];
                        }
                        $sendWelcomeData['attachments'][] = $link;
                        break;
                    case MediumType::MINI_PROGRAM:
                        // 上传临时素材
                        $mediaId = $mediaUtil->uploadImage($corpId, $content['mediumContent']['imagePath']);
                        $miniprogram['msgtype'] = 'miniprogram';
                        $miniprogram['miniprogram'] = [
                            'title' => $content['mediumContent']['title'],
                            'pic_media_id' => $mediaId,
                            'appid' => $content['mediumContent']['appid'],
                            'page' => $content['mediumContent']['page'],
                        ];

                        $sendWelcomeData['attachments'][] = $miniprogram;
                        break;
                }
            }
        }

        return $sendWelcomeData;
    }

    /**
     * 替换内容中的客户名称.
     */
    private function replaceContactName(array $content, string $contactName): array
    {
        if (isset($content['text']) && ! empty($content['text'])) {
            $content['text'] = str_replace(['##客户名称##', '用户昵称'], [$contactName, $contactName], $content['text']);
        }

        return $content;
    }

    /**
     * @param int|string $mediumId 素材库ID
     * @return array 响应数组
     */
    private function getMediums($mediumId): array
    {
        $mediumContent = [];
        if (is_numeric($mediumId)) {
            $medium = $this->mediumService->getMediumById((int) $mediumId, ['id', 'type', 'content']);
            return empty($medium) ? [] : [[
                'mediumType' => $medium['type'],
                'mediumContent' => Json::decode($medium['content'], true),
            ]];
        }
        $mediums = $mediumId;
        is_array($mediums) || $mediums = Json::decode((string) $mediumId);
        foreach ($mediums as $mediumInfo) {
            if ((int) $mediumInfo['type'] === 1) {
                $medium = $this->mediumService->getMediumById($mediumInfo['id'], ['id', 'type', 'content']);
                if (empty($medium)) {
                    continue;
                }
                $mediumContent[] = [
                    'mediumType' => $medium['type'],
                    'mediumContent' => Json::decode($medium['content'], true),
                ];
            } elseif ((int) $mediumInfo['type'] === 2) {
                $lottery = $this->lotteryService->getLotteryById($mediumInfo['id'], ['id', 'name', 'description']);
                if (empty($lottery)) {
                    continue;
                }
                $link = [
                    'title' => $lottery['name'],
                    'description' => $lottery['description'],
                    'imageLink' => Url::getAuthRedirectUrl(2, $mediumInfo['id'], ['source' => 'from_pc']),
                ];
                $mediumContent[] = [
                    'mediumType' => 3,
                    'mediumContent' => $link,
                ];
            }
        }
        return $mediumContent;
    }

    /**
     * 判断是否需要发送欢迎语.
     *
     * @return bool
     */
    private function isNeedSendWelcome(array $contact)
    {
        if (! isset($contact['state']) || empty($contact['state'])) {
            return false;
        }

        if (! isset($contact['welcomeCode']) || empty($contact['welcomeCode'])) {
            return false;
        }

        $stateArr = explode('-', $contact['state']);
        if ($stateArr[0] !== $this->getStateName()) {
            return false;
        }

        return true;
    }

    /**
     * 获取来源名称.
     *
     * @return string
     */
    private function getStateName()
    {
        return 'channelCode';
    }

    /**
     * 获取欢迎语.
     *
     * @param array $contact 客户
     *
     * @return array[]
     */
    private function getWelcome(array $contact): array
    {
        $stateArr = explode('-', $contact['state']);
        $channelCodeId = (int) $stateArr[1];

        $data = [];
        $channelCode = $this->channelCodeService->getChannelCodeById($channelCodeId, ['id', 'type', 'welcome_message', 'span_welcome_status', 'span_welcome', 'contact_remark', 'contact_description', 'close_words']);
        if (empty($channelCode)) {
            return $data;
        }

        // 客户备注、描述处理
        $this->handleContactRemarkDescription($contact, $channelCode);

        // 欢迎语类型1.渠道欢迎语，2.默认欢迎语，3.不发送欢迎语
        if ($channelCode['type'] !== 1) {
            return $data;
        }

        // 欢迎语屏蔽
        if (! empty($channelCode['closeWords'])) {
            $closeWords = Json::decode($channelCode['closeWords']);
            if ((int) $closeWords['status'] === 1) {
                foreach ($closeWords['words'] as $value) {
                    if(strpos($contact['name'], $value) !== false){
                        return $data;
                    }
                }

            }
        }

        // 分时段欢迎语开关关闭不发送 开 1-开 0-关 且普通欢迎语为空
        if ($channelCode['spanWelcomeStatus'] === 0 && empty($channelCode['welcomeMessage'])) {
            return $data;
        }

        // 分时段欢迎语开关打开，但是欢迎语为空
        if ($channelCode['spanWelcomeStatus'] === 1 && empty($channelCode['spanWelcome'])) {
            return $data;
        }

        if ($channelCode['spanWelcomeStatus'] === 1 && ! empty($channelCode['spanWelcome'])) {
            $spanWelcome = Json::decode($channelCode['spanWelcome']);
            // 分时段欢迎语开关为开
            if ($channelCode['spanWelcomeStatus'] === 1) {
                $spanWelcomeData = $this->getGreeting($spanWelcome);
                if (! empty($spanWelcomeData)) {
                    return $spanWelcomeData;
                }
            }
        }

        if (empty($channelCode['welcomeMessage'])) {
            return $data;
        }

        $welcomeMessage = Json::decode($channelCode['welcomeMessage'], true);
        if (isset($welcomeMessage['words'])) {// 新数据
            return [
                'text' => $welcomeMessage['words'],
                'mediumId' => $welcomeMessage['mediumId'],
            ];
        }

        return $data;
    }

    /**
     * 分时段欢迎语.
     * @param $spanWelcome
     */
    private function getGreeting($spanWelcome): array
    {
        $spanWelcomeData = [];
        foreach ($spanWelcome as $item) {
            // 判断是否在指定时间，指定星期内
            $time = date('H:i');
            if (isset($item['words'], $item['mediumId'], $item['startTime'], $item['endTime'], $item['week']) && $time > $item['startTime'] && $time < $item['endTime'] && in_array((int) date('w'), $item['week'], true)) {
                $spanWelcomeData = [
                    'text' => $item['words'],
                    'mediumId' => $item['mediumId'],
                ];
                break;
            }
        }
        if (! empty($spanWelcomeData) && isset($spanWelcomeData['mediumId']) && ! empty($spanWelcomeData['mediumId'])) {
            $medium = $this->getMedium((int) $spanWelcomeData['mediumId']);
            if (! empty($medium)) {
                $spanWelcomeData['medium'] = $medium;
            }
            unset($spanWelcomeData['mediumId']);
        }
        return $spanWelcomeData;
    }

    /**
     * @param int $mediumId 素材库ID
     * @return array 响应数组
     */
    private function getMedium(int $mediumId): array
    {
        $medium = $this->mediumService->getMediumById($mediumId, ['id', 'type', 'content']);
        return empty($medium) ? [] : [
            'mediumType' => $medium['type'],
            'mediumContent' => Json::decode($medium['content'], true),
        ];
    }

    private function handleContactRemarkDescription($contact, $channelCode): void
    {
        $corpId = (int) $contact['corpId'];
        $remark = '';
        $description = '';
        $contactEmployeeData = [];
        if (! empty($channelCode['contactRemark'])) {
            $contactRemark = Json::decode($channelCode['contactRemark']);
            if ((int) $contactRemark['status'] === 1) {
                if ((int) $contactRemark['type'] === 1) {
                    $remark = $contact['name'] . '-' . $contactRemark['remark'];
                } elseif ((int) $contactRemark['type'] === 2) {
                    $remark = $contactRemark['remark'] . '-' . $contact['name'];
                }

                $contactEmployeeData['remark'] = $remark;
            }
        }
        if (! empty($channelCode['contactDescription'])) {
            $description = $channelCode['contactDescription'];
            $contactEmployeeData['description'] = $description;
        }
        // 同步修改企业微信客户备注、描述
        if (! empty($remark) || ! empty($description)) {
            $this->workContactEmployeeService->updateWorkContactEmployeeByOtherIds($contact['employeeId'], $contact['id'], $contactEmployeeData);

            $employee = $this->workEmployeeService->getWorkEmployeeById($contact['employeeId'], ['wx_user_id']);
            if (! empty($employee)) {
                $data = [
                    'userid' => $employee['wxUserId'],
                    'external_userid' => $contact['wxExternalUserid'],
                ];
                empty($remark) || $data['remark'] = $remark;
                empty($description) || $data['description'] = $description;
                $weWorkApp = $this->weWorkFactory->getContactApp($corpId);
                $res = $weWorkApp->external_contact->remark($data);
                $this->logger->info('渠道活码客户描述同步结果：' . Json::encode($res));
            }
        }
    }
}
